/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  							 combat_terrain_map.cpp

	$Header: /heroes4/combat_terrain_map.cpp $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "combat_terrain_map.h"

#include "battlefield.h"
#include "battlefield_cell.h"
#include "combat_creature.h"
#include "combat_footprint.h"
#include "elements_of.h"
#include "enum_operations.h"
#include "hero.h"
#include "map_rect.h"
#include "skill.h"
#include "terrain.h"

// ------------------------------------------------------------------
// get corresponding bit for terrain
// ------------------------------------------------------------------
static int get_terrain_bit( t_terrain_type terrain )
{
	static int  bits[k_terrain_count];
	static bool initialized = false;

	if (!initialized)
	{
		static int const k_terrain_bits[][2] =
		{
			{ k_terrain_water,			k_combat_terrain_bit_water },
			{ k_terrain_grass,			k_combat_terrain_bit_grass },
			{ k_terrain_rough,			k_combat_terrain_bit_rough },
			{ k_terrain_swamp,			k_combat_terrain_bit_swamp },
			{ k_terrain_lava,			k_combat_terrain_bit_lava },
			{ k_terrain_snow,			k_combat_terrain_bit_snow },
			{ k_terrain_sand,			k_combat_terrain_bit_sand },
			{ k_terrain_dirt,			k_combat_terrain_bit_dirt },
			{ k_terrain_subterranean,	k_combat_terrain_bit_subterranean }
		};
		int				i;
		t_terrain_type  index;

		memset( bits, 0, sizeof( bits ));
		for (i = 0; i < ELEMENTS_OF( k_terrain_bits ); ++i)
		{
			bits[ k_terrain_bits[i][0] ] = k_terrain_bits[i][1];
		}
		for (index = k_basic_terrain_count; index < k_terrain_count; enum_incr(index))
		{
			bits[index] = bits[ get_basic_terrain( index ) ];
		}
		initialized = true;
	}
	return bits[terrain];
}

// ------------------------------------------------------------------
// get corresponding bit for terrain with brush
// ------------------------------------------------------------------
static int get_brush_terrain_bit( t_terrain_type terrain )
{
	static int  bits[k_terrain_count];
	static bool initialized = false;

	if (!initialized)
	{
		static int const k_terrain_bits[][2] =
		{
			{ k_terrain_grass,			k_combat_terrain_bit_grass_brush },
			{ k_terrain_rough,			k_combat_terrain_bit_rough_brush },
			{ k_terrain_swamp,			k_combat_terrain_bit_swamp_brush },
			{ k_terrain_lava,			k_combat_terrain_bit_lava_brush },
			{ k_terrain_snow,			k_combat_terrain_bit_snow_brush },
			{ k_terrain_sand,			k_combat_terrain_bit_sand_brush },
			{ k_terrain_dirt,			k_combat_terrain_bit_dirt_brush },
			{ k_terrain_subterranean,	k_combat_terrain_bit_subterranean_brush }
		};
		int				i;
		t_terrain_type  index;

		memset( bits, 0, sizeof( bits ));
		for (i = 0; i < ELEMENTS_OF( k_terrain_bits ); ++i)
		{
			bits[ k_terrain_bits[i][0] ] = k_terrain_bits[i][1];
		}
		for (index = k_basic_terrain_count; index < k_terrain_count; enum_incr(index))
		{
			bits[index] = bits[ get_basic_terrain( index ) ];
		}
		initialized = true;
	}
	return bits[terrain];
}

// ------------------------------------------------------------------
// determine correct flags to mark for a tile on the adventure map given
// a starting point
// ------------------------------------------------------------------
static t_uint32 get_terrain_flags( t_battlefield&			 battlefield,
								   t_map_point_2d const&	 point, 
								   t_combat_footprint const& footprint,
								   t_combat_creature const*	 army )
{
	// get cost of terrain
	int                       footprint_size = footprint.get_size();
	t_map_point_2d            offset;
	t_map_point_2d            new_point;
	int const*				  ptr = footprint.get();
	t_battlefield_cell const* cell;
	t_terrain_type			  terrain;
	t_uint32				  result = 0;
	
	for (offset.row = 0; offset.row < footprint_size; offset.row++)
	{
		for (offset.column = 0; offset.column < footprint_size; offset.column++)
		{
			if (!(*(ptr++)))
				continue;
			new_point = point + offset;
			if (!battlefield.is_valid( new_point ))
				continue;
			cell = &battlefield.get_cell( new_point );
			terrain = cell->get_terrain_type();
			if (cell->has_gate())
				terrain = k_terrain_grass;
			else if (army == 0 || cell->get_creature() != army)
			{
				if (terrain == k_terrain_water_river)
					result |= k_combat_terrain_bit_water;
				if (cell->is_quicksand())
					result |= k_combat_terrain_bit_quicksand;
			}
			if (cell->has_brush())
				result |= get_brush_terrain_bit( terrain );
			else
				result |= get_terrain_bit( terrain );
		}
	}
	return result;
}

// ------------------------------------------------------------------
// isometric map of bitsets showing for a given footprint, which terrains
// will be overlapped.
// ------------------------------------------------------------------
t_combat_terrain_map::t_combat_terrain_map( int footprint_size, t_battlefield& battlefield )
                    : t_isometric_map<t_uint32>( battlefield.get_size(), 
												 k_battlefield_cell_height, 
												 k_battlefield_logical_view_size, 0 ),
					  m_battlefield( battlefield )
{
	t_combat_footprint const&	footprint = get_combat_footprint( footprint_size );
	t_map_point_2d				point;
	int							row_end;
	t_uint32*					ptr;

	m_footprint_size = footprint_size;
	point.row = get_size();
	while (point.row--)
	{
		point.column = get_row_start( point.row );
		row_end = get_row_end( point.row );
		ptr = &get( point );
		while (point.column < row_end)
		{
			*ptr++ = get_terrain_flags( battlefield, point, footprint, 0 );
			++point.column;
		}
	}
}

// ------------------------------------------------------------------
// isometric map of bitsets showing for a given footprint, which terrains
// will be overlapped.
// ------------------------------------------------------------------
void t_combat_terrain_map::set_points( t_map_rect_2d const&		rect, 
									   t_combat_creature const* army )
{
	t_combat_footprint const&	footprint = get_combat_footprint( m_footprint_size );
	t_map_point_2d				point;
	int							bottom;
	int							row_start;
	int							row_end;
	t_uint32*					ptr;

	point.row = rect.top();
	if (point.row < 0)
		point.row = 0;
	bottom = rect.bottom();
	if (bottom > get_size())
		bottom = get_size();
	for (; point.row < bottom; ++point.row)
	{
		row_start = get_row_start( point.row );
		row_end = get_row_end( point.row );
		point.column = rect.left();
		if (point.column < row_start)
			point.column = row_start;
		if (row_end > rect.right())
			row_end = rect.right();
		ptr = &get( point );
		while (point.column < row_end)
		{
			*ptr++ = get_terrain_flags( m_battlefield, point, footprint, army );
			++point.column;
		}
	}
}


// ------------------------------------------------------------------
// isometric map of bitsets showing for a given footprint, which terrains
// will be overlapped.
// ------------------------------------------------------------------
void t_combat_terrain_map::refresh( t_combat_creature const* army )
{
	t_map_rect_2d rect;
	int			  footprint_size = army->get_footprint_size();
	
	rect.top_left = army->get_cell_position();
	rect.bottom_right.column = rect.top_left.column + m_footprint_size;
	rect.bottom_right.row = rect.top_left.row + m_footprint_size;
	refresh( rect, army );
}

// ------------------------------------------------------------------
// isometric map of bitsets showing for a given footprint, which terrains
// will be overlapped.
// ------------------------------------------------------------------
void t_combat_terrain_map::refresh( t_map_rect_2d const& rect, t_combat_creature const* army )
{
	t_map_rect_2d refresh_rect;

	refresh_rect.top_left.column = rect.top_left.column - m_footprint_size + 1;
	refresh_rect.top_left.row	 = rect.top_left.row - m_footprint_size + 1;
	refresh_rect.bottom_right    = rect.bottom_right;
	set_points( refresh_rect, army );
}


// ---------------------------------------------------------------
// compute the terrain cost and terrain bits 
// ---------------------------------------------------------------
void t_terrain_bit_cost_array::compute( t_combat_creature const& army )
{
	assert( size() == 0 );	// If you want to recompute, empty it first...

	static int const k_terrain_cost[k_basic_terrain_count] =
	{
		100, // k_terrain_water,
		100, // k_terrain_grass,
		125, // k_terrain_rough,
		150, // k_terrain_swamp,
		125, // k_terrain_lava,
		150, // k_terrain_snow,
		150, // k_terrain_sand,
		100, // k_terrain_dirt,
		100, // k_terrain_subterranean,
	};

	if (army.is_active( k_spell_terrain_walk ))
		return;

	t_hero const*   hero;
	t_skill_mastery mastery = k_mastery_none;

	hero = army.get_hero();
	if (hero != 0)
		mastery = hero->get_skill( k_skill_pathfinding );

	// these items are not affected by hero pathfinding skill
	// so they are always added
	int combat_movement = army.get_combat_movement();

	if (combat_movement > 200)
		push_back( t_terrain_bit_cost( k_combat_terrain_bit_quicksand, 
									   combat_movement >> 1 ) );
	if (combat_movement > 400)
		push_back( t_terrain_bit_cost( k_combat_terrain_bit_river,
									   combat_movement >> 2 ) );

	if (mastery < k_mastery_expert)
	{
		t_terrain_bit_cost  terrain_cost( 0, 100 );
		t_terrain_bit_cost  brush_cost( 0, 100 );
		t_terrain_type		terrain;

		for (terrain = k_terrain_grass; terrain < k_basic_terrain_count; enum_incr(terrain))
		{
			terrain_cost.bit = get_terrain_bit( terrain );
			terrain_cost.cost = k_terrain_cost[terrain];
			brush_cost.bit = get_brush_terrain_bit( terrain );
			brush_cost.cost = terrain_cost.cost + 50;
			if (army.is_native_terrain( terrain ))
			{
				terrain_cost.cost -= 25;
				brush_cost.cost -= 25;
			}
			if (mastery >= k_mastery_basic)
			{
				terrain_cost.cost -= 25;
				brush_cost.cost -= 25;
			} 
			else if (mastery >= k_mastery_advanced)
			{
				terrain_cost.cost -= 50;
				brush_cost.cost -= 50;
			}
			if ( terrain_cost.cost > 100 )
				push_back( terrain_cost );
			if ( brush_cost.cost > 100 )
				push_back( brush_cost );
		}
	}
	std::sort( begin(), end() );
}
